#include "MLInterStreamCommander.h"
#include "MLInterStreamAnalysis.h"
#include "MLInterStream.h"

CMLInterStreamCommander::CMLInterStreamCommander(void)
{
	m_pInterStream = NULL;
	m_pInterStreamAnalysis = NULL;
	m_bDataContainTimeStamp = false;
}

CMLInterStreamCommander::~CMLInterStreamCommander(void)
{
	if (m_pInterStream != NULL)
		delete m_pInterStream;
	if (m_pInterStreamAnalysis != NULL)
		delete m_pInterStreamAnalysis;
}

int CMLInterStreamCommander::Run(const std::vector<std::wstring>& vCommands)
{
	if (_wcsicmp(vCommands[0].c_str(), L"interanalysis") == 0)
	{
		return RunInterStreamAnalysis(vCommands);
	}
	else if (_wcsicmp(vCommands[0].c_str(), L"nbastats") == 0)
	{
		return RunNBAStatsDataConvert(vCommands);
	}
	else
	{
		std::wcerr<<L"Unknown command : "<<vCommands[0] << L"\n";
		_ASSERT(false);
		return -1;
	}
}

int CMLInterStreamCommander::RunInterStreamAnalysis(const std::vector<std::wstring>& vCommand)
{
	int nRet;
	int nComIndex = 1;
	if (vCommand.size() < 19)
	{
		std::cerr<<L"The commands is not completed!\n";
		_ASSERT(false);
		return -1;
	}
	// Input file and output file names
	m_wstrInputFileName  = vCommand[nComIndex++];
	m_wstrOutputFileName = vCommand[nComIndex++];
	if (_wcsicmp(vCommand[nComIndex].c_str(), L"@") == 0)
	{
		m_wstrSnapshotsFilename =L"";
		m_bInterAnalysis = true;
	}
	else
	{
		m_wstrSnapshotsFilename = vCommand[nComIndex];
		m_bInterAnalysis = false;
		nComIndex++;
	}
	m_bDataContainTimeStamp = _wcsicmp(vCommand[nComIndex++].c_str(), L"true") == 0;
	// Parameters for InterStreamAnalysis
	m_nDimension = _wtoi(vCommand[nComIndex++].c_str());
	m_nRegressP1 = _wtoi(vCommand[nComIndex++].c_str());
	m_nRegressP2 = _wtoi(vCommand[nComIndex++].c_str());
	m_nInterventionPosition = _wtoi(vCommand[nComIndex++].c_str());
	m_nPeriodStart = _wtoi(vCommand[nComIndex++].c_str());
	m_nPeriodEnd = _wtoi(vCommand[nComIndex++].c_str());
	// Parameters for InterStream
	m_DistWeights.resize(m_nDimension);
	if (_wcsicmp(vCommand[nComIndex].c_str(), L"noweights") == 0)
	{
		nComIndex++;
		for (int i=0;i<m_nDimension; i++)
			m_DistWeights[i] = 1;
	}
	else
	{
		for (int i=0;i<m_nDimension; i++)
			m_DistWeights[i] = _wtoi(vCommand[nComIndex++].c_str());
	}

	m_nInitNumClusters  = _wtoi(vCommand[nComIndex++].c_str());
	m_nMinNumClusters  = _wtoi(vCommand[nComIndex++].c_str());
	m_nMaxNumClusters  = _wtoi(vCommand[nComIndex++].c_str());
	m_nStreamBufferSize  = _wtoi(vCommand[nComIndex++].c_str());
	m_nStreamStartPos = _wtoi(vCommand[nComIndex++].c_str());
	m_nStreamMaxLength = _wtoi(vCommand[nComIndex++].c_str());
	m_nInitNumber = _wtoi(vCommand[nComIndex++].c_str());
	m_nTimeHorizon = _wtoi(vCommand[nComIndex++].c_str());
	m_nTimeUnit = _wtoi(vCommand[nComIndex++].c_str());
	m_nSizePerTime = _wtoi(vCommand[nComIndex++].c_str());
	m_dMaxRadius = _wtof(vCommand[nComIndex++].c_str());
	
	std::wcout<<L"InterStream starts.\n";

	// Initialize the InterStream
	nRet = InitInterStream();
	ML_ON_FAILED_RETURN(nRet);

	// Intialize the InterStreamAnalysis
	nRet = InitInterStreamAnalysis();
	ML_ON_FAILED_RETURN(nRet);

	// Initialize the data stream loader
	nRet = InitStreamLoader();
	ML_ON_FAILED_RETURN(nRet);

	// Initialize the result output file
	nRet = InitOutputFile();
	ML_ON_FAILED_RETURN(nRet);

	// Run InterStreamAnalysis
	nRet = RunInterStreamAnalysis();
	ML_ON_FAILED_RETURN(nRet);

	// Close 
	CloseAll();
	
	return 0;
}

int CMLInterStreamCommander::RunInterStreamAnalysis(void)
{
	int nRet;
	CMLDataSet dataSet;
	long nStartClock = 0;

	// Process data
	nRet = m_StreamLoader.Load(dataSet, m_nStreamBufferSize);
	if (ML_FAILED(nRet))
	{
		m_OutputFile.close();
		m_StreamLoader.Close();
		_ASSERT(false);
		return nRet;
	}

	int nDataIndex = 0;
	int nTime;
	int nStartTime = -1;
	int nLastTime = -1;
	CMLDataPoint dataPoint(m_nDimension);
	nStartClock = CMLTools::GetCurrentClock();

	while (dataSet.Count() > 0 )
	{
		int nPointIndex;
		for (nPointIndex=0; nPointIndex<dataSet.Count(); nPointIndex++)
		{
			const CMLDataPoint& srcPoint = dataSet.Get(nPointIndex);
			if (m_bDataContainTimeStamp)
			{
				for (int i=0;i<m_nDimension;i++)
					dataPoint[i] = srcPoint[i+1];
				nTime = (int)srcPoint[0];
				if (nStartTime == -1)
				{
					nStartTime = nTime;
					nLastTime = nTime;
				}
				m_pInterStreamAnalysis->Insert(dataPoint, nTime);
			}
			else
			{
				nTime = nDataIndex/m_nSizePerTime;
				m_pInterStreamAnalysis->Insert(srcPoint, nTime);
				if (nStartTime == -1)
				{
					nStartTime = nTime;
					nLastTime = nTime;
				}
			}
			nDataIndex++;

			if (nTime >= m_nPeriodStart)
			{
				if (m_bInterAnalysis) // intervention analysis
				{
					if ((nTime - nStartTime) % m_nTimeUnit ==0 && m_pInterStream->GetClusters().size() > 0)
					{
						m_pInterStream->MaintainClusters();
						m_OutputFile<<"Time :\t"<<nTime<<"\n";
						OutputClusters();
					}

					if (nTime == m_nPeriodEnd)
					{
						// Intervention Analysis
						nRet = m_pInterStreamAnalysis->InterventionAnalyze();
						if (ML_FAILED(nRet))
						{
							m_OutputFile.close();
							m_StreamLoader.Close();
							_ASSERT(false);
							return nRet;
						}
						goto _EXIT;

					}
				}
				else // snapshots storage
				{
					if (nTime == m_nPeriodEnd)
					{
						long nCurClock = CMLTools::GetCurrentClock();
						m_OutputFile << (nCurClock - nStartClock)/1000.0<<'\n';
						nRet = m_pInterStreamAnalysis->SaveClusterRecords(m_wstrSnapshotsFilename);
						goto _EXIT;
					}
				}

				nLastTime = nTime;
			}

			//if (nTime == m_nInterventionPosition)
			//{
			//	// Regress
			//	nRet = m_pInterStreamAnalysis->AutoRegress();
			//	if (ML_FAILED(nRet))
			//	{
			//		m_OutputFile.close();
			//		m_StreamLoader.Close();
			//		_ASSERT(false);
			//		return nRet;
			//	}
			//}
		}

		// Reload the data buffer
		nRet = m_StreamLoader.Load(dataSet, m_nStreamBufferSize);
		if (ML_FAILED(nRet))
		{
			m_OutputFile.close();
			m_StreamLoader.Close();
			_ASSERT(false);
			return nRet;
		}
	}

_EXIT:
	return 0;
}


int CMLInterStreamCommander::InitInterStream(void)
{
	m_pInterStream = new CMLInterStream();
	m_pInterStream->SetDistanceWeights(m_DistWeights);
	m_pInterStream->SetInitNumber(m_nInitNumber);
	m_pInterStream->SetNumInitClusters(m_nInitNumClusters);
	m_pInterStream->SetMaxNumClusters(m_nMaxNumClusters);
	m_pInterStream->SetMinNumClusters(m_nMinNumClusters);
	m_pInterStream->SetMaxRadius(m_dMaxRadius);
	m_pInterStream->SetTimeHorizon(m_nTimeHorizon);

	return 0;
}

int CMLInterStreamCommander::InitInterStreamAnalysis(void)
{
	m_pInterStreamAnalysis = new CMLInterStreamAnalysis(m_pInterStream, &m_OutputFile);
	m_pInterStreamAnalysis->SetDimension(m_nDimension);
	m_pInterStreamAnalysis->SetRegressParemters(m_nRegressP1, m_nRegressP2);
	m_pInterStreamAnalysis->SetTimeUnit(m_nTimeUnit);
	m_pInterStreamAnalysis->SetIntervetionPeriod(m_nPeriodStart, m_nPeriodEnd, m_nInterventionPosition);
	CMLDataPoint interCoeThreshold(m_nRegressP2);
	for (int i=0; i<interCoeThreshold.size(); i++)
		interCoeThreshold[i] = 0.0;
	interCoeThreshold[0] = 6.0; 
	m_pInterStreamAnalysis->SetCoeThreshold(interCoeThreshold);
	return 0;
}

int CMLInterStreamCommander::InitStreamLoader(void)
{
	// Load the input dataset
	int nRet = m_StreamLoader.Initialize(m_wstrInputFileName, m_nStreamStartPos, m_nStreamStartPos+m_nStreamMaxLength);
	if (ML_FAILED(nRet))
	{
		std::wcerr<< L"Cannot open the input dataset file : " << m_wstrInputFileName << L"\n";
		_ASSERT(false);
		return nRet;
	}
	return 0;
}

int CMLInterStreamCommander::InitOutputFile(void)
{
	m_OutputFile.open(m_wstrOutputFileName.c_str());
	if (m_OutputFile.fail())
	{
		_ASSERT(false);
		return -1;
	}
	return 0;
}

int CMLInterStreamCommander::CloseAll(void)
{
	delete m_pInterStream;
	m_pInterStream = NULL;
	delete m_pInterStreamAnalysis;
	m_pInterStreamAnalysis = NULL;

	m_StreamLoader.Close();
	m_OutputFile.close();
	return 0;
}

int CMLInterStreamCommander::OutputClusters()
{
	int i;
	std::vector<CMLInterStreamCluster*>& vpClusters = m_pInterStream->GetClusters();
	
	for (i=0; i<(int)vpClusters.size(); i++)
	{
		CMLDataPoint center = vpClusters[i]->GetCenter();
		double AvgTime = vpClusters[i]->GetAvgTime();
		CMLDataPoint LS = vpClusters[i]->GetLS();
		CMLDataPoint SS = vpClusters[i]->GetSS();
		m_OutputFile << AvgTime << L"\t";
		m_OutputFile << "id: "<<vpClusters[i]->GetID();
		m_OutputFile << " center: ";
		for (int j=0;j<center.size(); j++)
			m_OutputFile << center[j] << L"\t";
		m_OutputFile << " LS: ";
		for (int j=0;j<LS.size(); j++)
			m_OutputFile << LS[j] << L"\t";
		m_OutputFile << " SS: ";
		for (int j=0;j<SS.size(); j++)
			m_OutputFile << SS[j] << L"\t";
		m_OutputFile << " n: ";
		m_OutputFile << vpClusters[i]->GetSWLength();
		m_OutputFile << L"\n";
	}
	m_OutputFile << L"\n\n";
	return 0;
}

int CMLInterStreamCommander::RunNBAStatsDataConvert(const std::vector<std::wstring>& vCommand)
{
	int nRet;
	int nComIndex = 1;
	CMLNBAStatDataLoader nbaLoader;
	std::vector<CMLNBAStatDataLoader::PlayerSeasonData> players;
	CMLDataSet dataset;

	if (vCommand.size() < 5)
	{
		std::cerr<<L"The commands is not completed!\n";
		_ASSERT(false);
		return -1;
	}
	// Input file and output file names
	m_wstrInputFileName  = vCommand[nComIndex++];
	m_wstrOutputFileName = vCommand[nComIndex++];
	int nStartYear = _wtoi(vCommand[nComIndex++].c_str());
	int nEndYear = _wtoi(vCommand[nComIndex++].c_str());
	
	// Load NBA Players statis data
	nbaLoader.Initialize();
	nbaLoader.SetYearRange(nStartYear, nEndYear);
	nRet = nbaLoader.LoadPlayerRegularSeason(m_wstrInputFileName, players);
	ML_ON_FAILED_RETURN(nRet);
	dataset.Resize((int)players.size());
	CMLDataPoint point(7);

	// Convert the data strctures
	for (int i=0; i<(int)players.size(); i++)
	{
		const CMLNBAStatDataLoader::PlayerSeasonData& player = players[i];
		point[0] = player.year;
		point[1] = player.pts;
		point[2] = player.reb;
		point[3] = player.asts;
		point[4] = player.stl;
		point[5] = player.blk;
		point[6] = player.turnover;
		point.setid(i);
		dataset.Set(i, point);
	}

	// Save the dataset
	CMLTools_TxtFileSaver datasetSaver;
	datasetSaver.Initialize(m_wstrOutputFileName);
	datasetSaver.Save(dataset);
	datasetSaver.Close();
	std::wcout<<L"Convert OK!\n";

	return 0;
}
